package com.xunan.demo.config;

import com.alipay.api.AlipayClient;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Slf4j
@Configuration
public class AlipayPaymentConfig {

    /**
     * 支付宝网关地址
     */
    String GATEWAY_URL = "https://openapi.alipay.com/gateway.do";

    /**
     * 请求格式，固定值json
     */
    String REQUEST_FORMAT = "json";

    /**
     * 字符集
     */
    String REQUEST_CHARSET = "UTF-8";

    /**
     * 签名类型
     */
    String SIGN_TYPE = "RSA2";

    /**
     * 支付宝小程序AppID
     */
    @Value("${alipay-payment.alipay-mini-app.app-id}")
    String ALIPAY_MINI_APP_APP_ID;

    /**
     * 支付宝小程序应用私钥
     */
    @Value("${alipay-payment.alipay-mini-app.app-private-key-string}")
    String ALIPAY_MINI_APP_APP_PRIVATE_KEY_STRING;

    /**
     * 支付宝小程序应用公钥证书路径（绝对位置）
     */
    @Value("${alipay-payment.alipay-mini-app.app-public-cert-path}")
    String ALIPAY_MINI_APP_APP_PUBLIC_CERT_PATH;

    /**
     * 支付宝小程序支付宝公钥证书路径（绝对位置）
     */
    @Value("${alipay-payment.alipay-mini-app.alipay-public-cert-path}")
    String ALIPAY_MINI_APP_ALIPAY_PUBLIC_CERT_PATH;

    /**
     * 支付宝小程序支付宝根证书路径（绝对位置）
     */
    @Value("${alipay-payment.alipay-mini-app.alipay-root-cert-path}")
    String ALIPAY_MINI_APP_ALIPAY_ROOT_CERT_PATH;

    /**
     * Web、Wap、App程序AppID
     */
    @Value("${alipay-payment.web.app-id}")
    String WEB_APP_ID;

    /**
     * Web、Wap、App程序应用私钥
     */
    @Value("${alipay-payment.web.app-private-key-string}")
    String WEB_APP_PRIVATE_KEY_STRING;

    /**
     * Web、Wap、App程序应用公钥证书路径（绝对位置）
     */
    @Value("${alipay-payment.web.app-public-cert-path}")
    String WEB_APP_PUBLIC_CERT_PATH;

    /**
     * Web、Wap、App程序支付宝公钥证书路径（绝对位置）
     */
    @Value("${alipay-payment.web.alipay-public-cert-path}")
    String WEB_ALIPAY_PUBLIC_CERT_PATH;

    /**
     * Web、Wap、App程序支付宝根证书路径（绝对位置）
     */
    @Value("${alipay-payment.web.alipay-root-cert-path}")
    String WEB_ALIPAY_ROOT_CERT_PATH;

    /**
     * 支付宝通用证书客户端构造器
     * 参考文档：https://opendocs.alipay.com/open/204/105297   公钥证书模式加签（注意最后一句话）
     *
     * @param appID                应用ID
     * @param appPrivateKeyString  应用私钥
     * @param appPublicCertPath    应用公钥（路径）
     * @param alipayPublicCertPath 支付宝公钥（路径）
     * @param alipayRootCertPath   支付宝根证书（路径）
     * @return 支付宝通用证书客户端
     */
    private AlipayClient commonCertClientBuilder(String appID,
                                                 String appPrivateKeyString,
                                                 String appPublicCertPath,
                                                 String alipayPublicCertPath,
                                                 String alipayRootCertPath) {
        try {
            //构造client
            CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
            //设置网关地址
            certAlipayRequest.setServerUrl(GATEWAY_URL);
            //设置应用Id
            certAlipayRequest.setAppId(appID);

            //设置请求格式
            certAlipayRequest.setFormat(REQUEST_FORMAT);
            //设置字符集
            certAlipayRequest.setCharset(REQUEST_CHARSET);
            //设置签名类型
            certAlipayRequest.setSignType(SIGN_TYPE);

            //设置应用私钥
            certAlipayRequest.setPrivateKey(appPrivateKeyString);

            /*
             * 注意！谨慎使用setPath
             * 如果使用setPath，需要写绝对目录，而不能直接使用getResource。可以考虑使用getResourceAsStream
             * 但是必须要注意的是，验签使用的AlipaySignature.rsaCertCheckV1并不支持使用setContent或者getResourceAsStream
             *
             * 由于支付宝在读取秘钥的时候使用了File，其是将File文件转为了FileInputStream
             * 然而，在部署环境下，this.getClass().getResource(WEB_APP_PUBLIC_CERT_PATH).getPath()获取到的路径往往是在jar文件下
             * file:/code/target/pro-trueziroemdemo-boot-serverless.jar!/BOOT-INF/classes!/alipay-payment-cert-web/alipay-root-cert.crt
             *
             * 可见如下解释：
             * 1
             * resource.getFile() expects the resource itself to be available on the file system, i.e. it can't be nested inside a jar file.
             * This is why it works when you run your application in STS (Spring Tool Suite) but doesn't work once you've built your application and run it from the executable jar.
             * Rather than using getFile() to access the resource's contents, I'd recommend using getInputStream() instead.
             * That'll allow you to read the resource's content regardless of where it's located.
             *
             * 2
             * A java.io.File represents a file on the file system, in a directory structure.
             * The Jar is a java.io.File.
             * But anything within that file is beyond the reach of java.io.File.
             * As far as java is concerned, until it is uncompressed, a class in jar file is no different than a word in a word document.
             *
             * 相同情况亦可发生在使用Resource resource = new ClassPathResource(this.getClass().getResource(WEB_ALIPAY_ROOT_CERT_PATH).getPath());时
             *
             * 参考：https://stackoverflow.com/questions/25869428/classpath-resource-not-found-when-running-as-jar
             * 参考：https://stackoverflow.com/questions/14876836/file-inside-jar-is-not-visible-for-spring
             *
             * */

            //设置应用公钥证书路径（绝对位置）
            certAlipayRequest.setCertPath(appPublicCertPath);
            ////设置应用公钥证书路径（Resource路径）
            //InputStream certInputStream = this.getClass().getResourceAsStream(appPublicCertPath);
            //String certContent;
            //if (certInputStream != null) {
            //    certContent = IOUtils.toString(certInputStream, StandardCharsets.UTF_8);
            //    log.info("读取应用公钥证书成功");
            //    certAlipayRequest.setCertContent(certContent);
            //} else {
            //    log.info("读取应用公钥证书失败");
            //}

            //设置支付宝公钥证书路径（绝对位置）
            certAlipayRequest.setAlipayPublicCertPath(alipayPublicCertPath);
            ////设置支付宝公钥证书路径（Resource路径）
            //InputStream alipayPublicCertInputStream = this.getClass().getResourceAsStream(alipayPublicCertPath);
            //String alipayPublicCertContent;
            //if (alipayPublicCertInputStream != null) {
            //    alipayPublicCertContent = IOUtils.toString(alipayPublicCertInputStream, StandardCharsets.UTF_8);
            //    log.info("读取支付宝公钥证书成功");
            //    certAlipayRequest.setAlipayPublicCertContent(alipayPublicCertContent);
            //} else {
            //    log.info("读取支付宝公钥证书失败");
            //}
            //

            //设置支付宝根证书路径（绝对位置）
            certAlipayRequest.setRootCertPath(alipayRootCertPath);
            ////设置支付宝根证书路径（Resource路径）
            //InputStream alipayRootCertInputStream = this.getClass().getResourceAsStream(alipayRootCertPath);
            //String alipayRootCertContent;
            //if (alipayRootCertInputStream != null) {
            //    alipayRootCertContent = IOUtils.toString(alipayRootCertInputStream, StandardCharsets.UTF_8);
            //    log.info("读取支付宝根证书成功");
            //    certAlipayRequest.setRootCertContent(alipayRootCertContent);
            //} else {
            //    log.info("读取支付宝根证书失败");
            //}

            //构造client
            return new DefaultAlipayClient(certAlipayRequest);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 支付宝网页Client构造器
     *
     * @return 支付宝Client
     */
    @Bean
    public AlipayClient webAlipayClient() {
        try {
            return commonCertClientBuilder(
                    WEB_APP_ID,
                    WEB_APP_PRIVATE_KEY_STRING,
                    WEB_APP_PUBLIC_CERT_PATH,
                    WEB_ALIPAY_PUBLIC_CERT_PATH,
                    WEB_ALIPAY_ROOT_CERT_PATH);
        } catch (Exception exception) {
            exception.printStackTrace();
            return null;
        }
    }

    /**
     * 支付宝小程序Client构造器
     *
     * @return 支付宝Client
     */
    @Bean
    public AlipayClient alipayMiniAppAlipayClient() {
        try {
            return commonCertClientBuilder(
                    ALIPAY_MINI_APP_APP_ID,
                    ALIPAY_MINI_APP_APP_PRIVATE_KEY_STRING,
                    ALIPAY_MINI_APP_APP_PUBLIC_CERT_PATH,
                    ALIPAY_MINI_APP_ALIPAY_PUBLIC_CERT_PATH,
                    ALIPAY_MINI_APP_ALIPAY_ROOT_CERT_PATH);
        } catch (Exception exception) {
            exception.printStackTrace();
            return null;
        }
    }

}
